# 【优化版】动作链案例 - 解决页面刷新后元素失效问题
import time
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 加载浏览器驱动
executable_path = "./chromedriver.exe"
service = Service(executable_path=executable_path)
browser = Chrome(service=service)
wait = WebDriverWait(browser, 10)

# 【方案一】封装元素定位器类 - 推荐方案
class ElementLocator:
    """元素定位器类，自动处理页面刷新后的元素重新定位"""
    
    def __init__(self, browser, by, value, description=""):
        self.browser = browser
        self.by = by
        self.value = value
        self.description = description
        self._element = None
    
    def get_element(self):
        """获取元素，如果元素失效则重新定位"""
        try:
            # 检查元素是否还有效
            if self._element and self._element.is_enabled():
                return self._element
        except:
            # 元素失效，需要重新定位
            pass
        
        # 重新定位元素
        self._element = wait.until(EC.presence_of_element_located((self.by, self.value)))
        print(f"重新定位元素: {self.description}")
        return self._element
    
    def __repr__(self):
        return f"ElementLocator({self.description})"

# 【方案二】装饰器方案
def auto_relocate(func):
    """装饰器：自动处理元素失效并重新定位"""
    def wrapper(*args, **kwargs):
        max_retries = 3
        for attempt in range(max_retries):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                if "stale element" in str(e).lower() and attempt < max_retries - 1:
                    print(f"检测到元素失效，第{attempt + 1}次重试...")
                    # 重新定位所有元素
                    relocate_all_elements()
                    time.sleep(1)
                else:
                    raise e
    return wrapper

# 【方案三】元素管理器类
class ElementManager:
    """元素管理器，统一管理所有元素的定位"""
    
    def __init__(self, browser):
        self.browser = browser
        self.wait = WebDriverWait(browser, 10)
        self.locators = {
            'dragger': (By.ID, "dragger"),
            'target1': (By.CSS_SELECTOR, "body > div:nth-child(4)"),
            'target2': (By.CSS_SELECTOR, "body > div:nth-child(5)"),
            'target3': (By.CSS_SELECTOR, "body > div:nth-child(6)"),
            'target4': (By.CSS_SELECTOR, "body > div:nth-child(7)")
        }
        self.elements = {}
    
    def get_element(self, name):
        """获取元素，自动处理失效情况"""
        try:
            # 检查缓存的元素是否还有效
            if name in self.elements:
                element = self.elements[name]
                element.is_enabled()  # 测试元素是否有效
                return element
        except:
            # 元素失效，从缓存中移除
            if name in self.elements:
                del self.elements[name]
        
        # 重新定位元素
        by, value = self.locators[name]
        element = self.wait.until(EC.presence_of_element_located((by, value)))
        self.elements[name] = element
        print(f"重新定位元素: {name}")
        return element
    
    def refresh_all_elements(self):
        """刷新所有元素"""
        self.elements.clear()
        print("清空所有元素缓存")

# 初始化元素管理器
element_manager = ElementManager(browser)

# 打开网址
browser.get("http://sahitest.com/demo/dragDropMooTools.htm")
time.sleep(3)

# 【使用方案一：ElementLocator类】
print("=== 使用ElementLocator类 ===")
dragger_locator = ElementLocator(browser, By.ID, "dragger", "拖拽元素")
target1_locator = ElementLocator(browser, By.CSS_SELECTOR, "body > div:nth-child(4)", "目标1")

# 第一次拖拽
actions = ActionChains(browser)
actions.drag_and_drop(dragger_locator.get_element(), target1_locator.get_element()).perform()
time.sleep(2)

# 刷新页面
print("刷新页面...")
browser.refresh()
time.sleep(3)

# 页面刷新后，元素会自动重新定位
actions = ActionChains(browser)
actions.drag_and_drop(dragger_locator.get_element(), target1_locator.get_element()).perform()
time.sleep(2)

# 【使用方案三：ElementManager】
print("=== 使用ElementManager ===")

# 刷新页面
browser.refresh()
time.sleep(3)
element_manager.refresh_all_elements()  # 手动清空缓存

# 使用元素管理器进行拖拽
actions = ActionChains(browser)
actions.drag_and_drop(
    element_manager.get_element('dragger'), 
    element_manager.get_element('target2')
).perform()
time.sleep(2)

# 再次刷新页面
browser.refresh()
time.sleep(3)

# 元素管理器会自动处理失效的元素
actions = ActionChains(browser)
actions.drag_and_drop(
    element_manager.get_element('dragger'), 
    element_manager.get_element('target3')
).perform()
time.sleep(2)

print("演示完成！")
time.sleep(3)
browser.quit()